﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using PdfSharp.Pdf;
using PdfSharp.Drawing;
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Tables;
using MigraDoc.Rendering;

namespace DestinationPDF
{
    /// <summary>
    /// Object what exports to PDF objects like HTML complex struct, specific HTML widgets, text blocks, etc.
    /// </summary>
    public class DestinationPDF
    {
        private PdfDocument Doc;
        private PDFDocumentAttributes DocAttributes;
        private List<PDFWidget> Widgets;

        public struct PDFDocumentAttributes
        {
            public string FileName;
            public double? MarginTop;
            public double? MarginLeft;
            public double? MarginBottom;
            public double? MarginRight;
        }
        public struct DestinationPDFParameters
        {
            public PDFDocumentAttributes DocAttributes;
            public List<PDFWidget> Widgets;
        }

        public DestinationPDF()
        {
        }
        /// <summary>
        /// Destination PDF constructor.
        /// </summary>
        /// <param name="widgetsList">List of PDF Widgets what will be exported to PDF.</param>
        public DestinationPDF(List<PDFWidget> widgetsList)
        {
            Initialize(widgetsList);
            DrawPdfFile();
        }
        /// <summary>
        /// Destination PDF constructor.
        /// </summary>
        /// <param name="widgetsList">List of PDF Widgets what will be exported to PDF.</param>
        /// <param name="docAttribs">Attrbites of the PDF document.</param>
        public DestinationPDF(List<PDFWidget> widgetsList, PDFDocumentAttributes docAttribs)
        {
            Initialize(widgetsList,docAttribs);
            DrawPdfFile();
        }

        private void Initialize(List<PDFWidget> widgetsList)
        {
            try
            {
                Doc = new PdfDocument();
                DocAttributes.FileName = System.Text.RegularExpressions.Regex.Match(Path.GetTempFileName(), "(?!.*\\\\).*\\.").Value + "pdf";

                Widgets = widgetsList;
            }
            catch (Exception ex) { }
        }
        private void Initialize(List<PDFWidget> widgetsList, PDFDocumentAttributes docAttribs)
        {
            try
            {
                Doc = new PdfDocument();
                DocAttributes = docAttribs;
                DocAttributes.FileName = (DocAttributes.FileName != null) ? DocAttributes.FileName : System.Text.RegularExpressions.Regex.Match(Path.GetTempFileName(),"(?!.*\\\\).*\\.").Value + "pdf";
                Widgets = widgetsList;
            }
            catch (Exception ex) { }
        }

        private void DrawPdfFile()
        {
            try
            {
                System.Web.Script.Serialization.JavaScriptSerializer jss = new System.Web.Script.Serialization.JavaScriptSerializer();

                Document DocMigra = new Document();
                    Section sec = DocMigra.AddSection();
                    sec.PageSetup.TopMargin = (DocAttributes.MarginTop != null) ? new Unit((double)DocAttributes.MarginTop) : sec.PageSetup.TopMargin;
                    sec.PageSetup.LeftMargin = (DocAttributes.MarginLeft != null) ? new Unit((double)DocAttributes.MarginLeft) : sec.PageSetup.LeftMargin;
                    sec.PageSetup.BottomMargin = (DocAttributes.MarginBottom != null) ? new Unit((double)DocAttributes.MarginBottom) : sec.PageSetup.BottomMargin;
                    sec.PageSetup.RightMargin = (DocAttributes.MarginRight != null) ? new Unit((double)DocAttributes.MarginRight) : sec.PageSetup.RightMargin;
                        Table tableLayout = sec.AddTable();
                            Column col = tableLayout.AddColumn();
                            col.Width = DocMigra.DefaultPageSetup.PageWidth - DocMigra.DefaultPageSetup.LeftMargin - DocMigra.DefaultPageSetup.RightMargin;
                DocumentRenderer renderer = new DocumentRenderer(DocMigra);

                foreach (PDFWidget pdfWidget in Widgets)
                {
                    switch (pdfWidget.Type)
                    {
                        case PDFWidgetType.HTMLStruct:
                            HTMLStruct hs = jss.Deserialize<HTMLStruct>(pdfWidget.WidgetData);
                            hs.DrawToPDF(DocMigra, tableLayout, pdfWidget.Attributes);
                            break;
                        case PDFWidgetType.HTMLText:
                            HTMLText ht = jss.Deserialize<HTMLText>(pdfWidget.WidgetData);
                            ht.DrawToPDF(DocMigra, tableLayout, pdfWidget.Attributes);
                            break;
                    }
                }

                renderer.PrepareDocument();

                int pageCount = renderer.FormattedDocument.PageCount;
                for (int iPage = 0; iPage < pageCount; iPage++ )
                {
                    PdfPage page = new PdfPage();
                    Doc.AddPage(page);
                        XGraphics gfx = XGraphics.FromPdfPage(page);
                        gfx.MUH = PdfFontEncoding.Unicode;
                        gfx.MFEH = PdfFontEmbedding.Default;
                    renderer.RenderPage(gfx, iPage + 1);
                }
            }
            catch (Exception ex) { }
        }
        /// <summary>
        /// Get the pdf file's name.
        /// </summary>
        /// <returns>The pdf file's name.</returns>
        public string GetFileName()
        {
            return DocAttributes.FileName;
        }
        /// <summary>
        /// Save the pdf document in the specified path.
        /// </summary>
        /// <param name="path">Path where the pdf document will be stored.</param>
        public void Save(string path)
        {
            try
            {
                Doc.Save(path);
            }
            catch (Exception ex) { }
        }
        /// <summary>
        /// Save the pdf document in a stream.
        /// </summary>
        /// <param name="stream">Stream where the pdf document will be stored.</param>
        public void Save(Stream stream)
        {
            try
            {
                MemoryStream ms = new MemoryStream();
                Doc.Save(ms,false);
                ms.WriteTo(stream);
            }
            catch (Exception ex) { }
        }
    }

    /// <summary>
    /// Interface used by the PDF Widgets to export itself to pdf.
    /// </summary>
    public interface IDestinationPDF
    {
        /// <summary>
        /// Draw to pdf the PDF Widget.
        /// </summary>
        /// <param name="DocMigra">Migradoc Document object where all widgets will be added and then rendered to PDFSharp pdf document.</param>
        /// <param name="tableLayout">Migradoc Table object what work llike a layout of each pdf page.</param>
        /// <param name="pdfWidgetAttribs"Draw attributes of the PDF Widgets.></param>
        void DrawToPDF(Document DocMigra, Table tableLayout, PDFWidget.PDFWidgetAttributes pdfWidgetAttribs);
    }

    /// <summary>
    /// Enum of PDF Widgets Types.
    /// </summary>
    public enum PDFWidgetType
    {
        HTMLText
        ,HTMLStruct
    }
    /// <summary>
    /// Object what will be drawing to the pdf document.
    /// </summary>
    public class PDFWidget
    {
        public PDFWidgetType Type;
        public string WidgetData;
        public PDFWidgetAttributes Attributes;

        public struct PDFWidgetAttributes
        {
            public double? Width;
            public double? Height;
        }

        public PDFWidget()
        {
        }
    }

    /// <summary>
    /// Object what contains an HTML complex struct for draw it to PDF.
    /// </summary>
    public class HTMLStruct : IDestinationPDF
    {
        public string HTMLString;
        public string FullPath;

        public HTMLStruct()
        {
        }
        public HTMLStruct(string fullPath)
        {
            FullPath = fullPath;
        }
        public HTMLStruct(string fullPath, string htmlString)
        {
            FullPath = fullPath;
            HTMLString = htmlString;
        }

        public void DrawToPDF(Document DocMigra, Table tableLayout, PDFWidget.PDFWidgetAttributes pdfWidgetAttribs)
        {
            try
            {
                Bitmap bitmap = HTMLShot.GetHTMLShot(FullPath, HTMLString, (int?)pdfWidgetAttribs.Width, (int?)pdfWidgetAttribs.Height);
                string imagePath = Path.GetTempFileName().Replace("tmp", "png");
                bitmap.Save(imagePath, System.Drawing.Imaging.ImageFormat.Png);
                bitmap.Dispose();

                Row row = tableLayout.AddRow();
                Cell cell = new Cell();
                cell.AddImage(imagePath);
                row.Cells.Add(cell);
            }
            catch (Exception ex) { }
        }
    }

    /// <summary>
    /// Object what contains a text block without format for draw it to PDF.
    /// </summary>
    public class HTMLText : IDestinationPDF
    {
        public string Value;
        public HTMLText()
        {
        }
        public HTMLText(string value)
        {
            Value = value;
        }

        public void DrawToPDF(Document DocMigra, Table tableLayout, PDFWidget.PDFWidgetAttributes pdfWidgetAttribs)
        {
            try
            {
                Row row = tableLayout.AddRow();
                Cell cell = new Cell();
                Paragraph paragraph = cell.AddParagraph();
                paragraph.AddFormattedText(Value);
                row.Cells.Add(cell);
            }
            catch (Exception ex) { }
        }
    }
}
